/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package de.editura.common.swing.imageviewer;

import de.editura.common.filefilter.ImageFileFilter;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.font.TextAttribute;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.text.AttributedString;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import javax.swing.JComponent;

/**
 * 
 * @author kschmidtner
 */
public class ImageCanvas extends JComponent implements MouseListener, MouseMotionListener, MouseWheelListener {

	ImageViewerPanel imgVPanel = null;
	int canvasHeight = 700;
	int canvasWidth = 490;
	float ratio = calculateRatio();
	float zoomRatio = 1;
	int zoomX = 0;
	int zoomSensitivity = 20;
	int offX = 0;
	int offY = 0;
	int mousePosX = 0;
	int mousePosY = 0;
	int mousePosXDiff = 0;
	int mousePosYDiff = 0;

	File imgFile = new File("");
	BufferedImage img;
	List<TextBoxOnImage> textBoxes = new ArrayList<TextBoxOnImage>();
	TextBoxOnImage currentTBOI = null;

	@Override
	public void paint(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;

		g2.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
		g2.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
		g2.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
		g2.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

		this.canvasHeight = this.getHeight();
		this.canvasWidth = this.getWidth();
		this.ratio = calculateRatio();

		g2.drawImage(img, 0, 0, canvasWidth, canvasHeight, offX, offY, (img.getWidth() - zoomX) + offX,
				(int) ((img.getWidth() - zoomX) / ratio) + offY, null);

		g2.drawImage(img, offX, offX, offX, offX, offX, offX, offX, offX, this);

		for (TextBoxOnImage tBOI : textBoxes) {
			AttributedString as = new AttributedString(tBOI.getText());
			as.addAttribute(TextAttribute.FONT, tBOI.getFont());
			as.addAttribute(TextAttribute.FOREGROUND, tBOI.getColor());

			int tboxX = (int) ((tBOI.getTextPosX() - offX + 0.0f) / zoomRatio);
			int tboxY = (int) ((tBOI.getTextPosY() - offY + 0.0f) / zoomRatio);
			g2.drawString(as.getIterator(), tboxX, tboxY);

			// FontMetrics metrics = g.getFontMetrics(tBOI.getFont());
			// int hgt = metrics.getHeight();
			// int adv = metrics.stringWidth(tBOI.getText());
			// g2.fillRect(tboxX, tboxY, adv, hgt);
		}

		if (imgVPanel != null) {
			imgVPanel.getImgCanvasSizeTF().setText(
					"canvasHeight: " + canvasHeight + " canvasWidth: " + canvasWidth + " - ratio: " + ratio
							+ " - imageName: " + imgFile.getName());
		}
		// System.out.println("zoomX: " + zoomX);
		// System.out.println("canvasHeight: " + canvasHeight + " canvasWidth: "
		// + canvasWidth);
		// System.out.println("Width: " + this.getWidth() + " Height: " +
		// this.getHeight());
	}

	public ImageCanvas() {
		addMouseMotionListener(this);
		addMouseListener(this);
		addMouseWheelListener(this);

		try {
			URL url = this.getClass().getResource("resources/imgCanvasDummy.jpg");
			img = ImageIO.read(url);
		} catch (IOException e) {
		}
		this.setZoomSensibility();
	}

	public ImageCanvas(File f) {
		this.imgFile = f;
		try {
			img = ImageIO.read(f);
		} catch (IOException e) {
		}
		this.setZoomSensibility();
		textBoxes = new ArrayList<TextBoxOnImage>();
	}

	public ImageCanvas(BufferedImage img) {
		this.img = img;
		this.setZoomSensibility();
		textBoxes = new ArrayList<TextBoxOnImage>();
	}

	public int getZoomX() {
		return zoomX;
	}

	public void setZoomX(int zoomX) {
		this.zoomX = zoomX;
		this.zoomRatio = (float) ((img.getWidth() - zoomX) + 0.0f) / canvasWidth;
	}

	public float getZoomRatio() {
		return zoomRatio;
	}

	public void setZoomRatio(float zoomRatio) {
		this.zoomRatio = zoomRatio;
	}

	public int getOffX() {
		return offX;
	}

	public void setOffX(int offX) {
		this.offX = offX;
	}

	public int getOffY() {
		return offY;
	}

	public void setOffY(int offY) {
		this.offY = offY;
	}

	public BufferedImage getImg() {
		return img;
	}

	public void setImg(BufferedImage img) {
		this.img = img;
		if (img != null) {
			this.setZoomSensibility();
		}
		textBoxes = new ArrayList<TextBoxOnImage>();
	}

	public void setImgWithFile(BufferedImage img, File imgF) {
		this.img = img;
		this.imgFile = imgF;
		if (img != null) {
			this.setZoomSensibility();
		}
		textBoxes = new ArrayList<TextBoxOnImage>();
	}

	public void refreshImgFile(File imgFile) {
		this.imgFile = imgFile;
		try {
			img = ImageIO.read(imgFile);
		} catch (IOException e) {
		}
		this.setZoomSensibility();
		textBoxes = new ArrayList<TextBoxOnImage>();
		this.repaint();
	}

	public void refreshImgFile(URL fileUrl) {
		try {
			img = ImageIO.read(fileUrl);
		} catch (IOException e) {
			e.printStackTrace();
		}
		this.setZoomSensibility();
		textBoxes = new ArrayList<TextBoxOnImage>();
		this.repaint();
	}

	public void showNextImage() {
		File nextImg = takeImgForShow(1);
		this.refreshImgFile(nextImg);
	}

	public void showPreviousImage() {
		File prevImg = takeImgForShow(-1);
		this.refreshImgFile(prevImg);
	}

	private File takeImgForShow(int step) {
		// System.out.println("imgFile" + imgFile.getName());
		File imgDir = imgFile.getParentFile();
		// System.out.println("imgDir: " + imgDir.getName());
		File returnImg = this.imgFile;
		File[] files = imgDir.listFiles(new ImageFileFilter());
		// System.out.println("File length: " + files.length);
		int pos = 0;
		int aktPosition = 0;
		int wantedPosition = 0;

		if (files.length > 0) {
			for (File file : files) {
				if (file.getName().equals(imgFile.getName())) {
					aktPosition = pos;
				}
				pos++;
			}
			wantedPosition = aktPosition + step;

			if (wantedPosition <= 0) {
				wantedPosition = 0;
			} else if (wantedPosition >= files.length - 1) {
				wantedPosition = files.length - 1;
			}

			returnImg = files[wantedPosition];
		}

		return returnImg;
	}

	public int getZoomSensibility() {
		return zoomSensitivity;
	}

	public void setZoomSensibility(int zoomSensibility) {
		this.zoomSensitivity = zoomSensibility;
	}

	private void setZoomSensibility() {
		int sensibility = img.getWidth() / 20;
		// System.out.println("width: " + img.getWidth());
		// System.out.println("sensi: " + sensibility);
		this.setZoomSensibility(sensibility);
	}

	public void refreshAsStandard() {
		this.zoomX = 0;
		this.zoomRatio = 1f;
		this.offX = 0;
		this.offY = 0;
		this.repaint();
	}

	public List<TextBoxOnImage> getTextBoxes() {
		return textBoxes;
	}

	public void setTextBoxes(List<TextBoxOnImage> textBoxes) {
		this.textBoxes = textBoxes;
	}

	// Move Methods
	public void zoomIn() {
		this.setZoomX(zoomX + zoomSensitivity);
		this.repaint();
	}

	public void zoomOut() {
		this.setZoomX(zoomX - zoomSensitivity);
		this.repaint();
	}

	public void moveLeft(int horizontal) {
		this.offX += horizontal;
		this.repaint();
	}

	public void moveRight(int horizontal) {
		this.offX += horizontal;
		this.repaint();
	}

	public void moveUp(int vertical) {
		this.offY += vertical;
		this.repaint();
	}

	public void moveDown(int vertical) {
		this.offY += vertical;
		this.repaint();
	}

	// MouseListener
	public void mouseClicked(MouseEvent e) {
		// mousePosX = e.getX();
		// mousePosY = e.getY();
		// System.out.println("posX: " + mousePosX);
		// System.out.println("posY: " + mousePosY);
	}

	public void mousePressed(MouseEvent e) {
		mousePosX = e.getX();
		mousePosY = e.getY();
	}

	public void mouseReleased(MouseEvent e) {
		mousePosX = e.getX();
		mousePosY = e.getY();
	}

	public void mouseEntered(MouseEvent e) {
		// throw new UnsupportedOperationException("Not supported yet.");
	}

	public void mouseExited(MouseEvent e) {
		// throw new UnsupportedOperationException("Not supported yet.");
	}

	public void mouseDragged(MouseEvent e) {
		if (isMouseOverText(e.getX(), e.getY())) {
			// System.out.println("Ich bin in der Box");
			moveTextBox(e.getX(), e.getY());
			this.mousePosX = e.getX();
			this.mousePosY = e.getY();
		} else {
			moveBackgroundImage(e);
		}
	}

	public void mouseMoved(MouseEvent e) {
		// System.out.println("BEWEGUNG");
	}

	public void mouseWheelMoved(MouseWheelEvent e) {
		int notches = e.getWheelRotation();

		if (notches < 0) {
			// Wheel moved up
			this.zoomIn();
		} else {
			// wheel moved down
			this.zoomOut();
		}
	}

	// Custom Methods
	private boolean isMouseOverText(int x, int y) {
		for (TextBoxOnImage tBOI : textBoxes) {
			Graphics g = this.getGraphics();
			FontMetrics metrics = g.getFontMetrics(tBOI.getFont());
			// get the height of a line of text in this font and render context
			int hgt = metrics.getHeight();
			// get the advance of my text in this font and render context
			int adv = metrics.stringWidth(tBOI.getText());
			// calculate the size of a box to hold the text with some padding.
			Rectangle area = new Rectangle(tBOI.getTextPosX(), tBOI.getTextPosY(), adv + 20, hgt + 20);

			if (area.contains(x, y)) {
				currentTBOI = tBOI;
				System.out.println(currentTBOI.getText());
				return true;
			}
		}
		currentTBOI = null;
		return false;
	}

	public void moveBackgroundImage(MouseEvent e) {

		int horizontal = mousePosX - e.getX();
		int vertical = mousePosY - e.getY();
		if (horizontal > 0) {
			this.moveLeft(horizontal);
		}
		if (horizontal < 0) {
			this.moveRight(horizontal);
		}

		if (vertical > 0) {
			this.moveUp(vertical);
		}

		if (vertical < 0) {
			this.moveDown(vertical);
		}
		this.mousePosX = e.getX();
		this.mousePosY = e.getY();

	}

	public void moveTextBox(int x, int y) {
		// System.out.println("Method - MOVETEXTBOX");
		float newX = (x * zoomRatio) + this.getOffX();
		float newY = (y * zoomRatio) + this.getOffY();

		currentTBOI.setTextPosX((int) newX);
		currentTBOI.setTextPosY((int) newY);
		this.repaint();
	}

	private float calculateRatio() {
		float ratio = (float) this.canvasWidth / (float) this.canvasHeight;
		return ratio;
	}

	public ImageViewerPanel getImgVPanel() {
		return imgVPanel;
	}

	public void setImgVPanel(ImageViewerPanel imgVPanel) {
		this.imgVPanel = imgVPanel;
	}
}
